package references

import (
	"jvm/nzpJvm/instructions/base"
	"jvm/nzpJvm/runtime_area"
	"jvm/nzpJvm/runtime_area/heap"
)

// 调用接口方法
type INVOKE_INTERFACE struct {
	index uint
}

func (self *INVOKE_INTERFACE)FetchOperands(reader *base.BytecodeReader)  {
	// 两字节的常量池索引
	self.index = uint(reader.ReadUint16())
	// 方法传递参数所需的slot数，这个完全可以根据方法的描述符计算出来，它的存在是历史原因，为了向下兼容，所以不用它。
	reader.ReadUint8()
	// 保留字节，目前必须是0
	reader.ReadUint8()
}

func (self *INVOKE_INTERFACE) Execute(frame *runtime_area.Frame) {
	constantPool := frame.Method().Class().ConstantPool()
	// 方法的符号引用
	methodRef := constantPool.GetConstant(self.index).(*heap.InterfaceMethodRef)
	method := methodRef.ResolveInterfaceMethod()
	// 接口方法必须是public的
	if !method.IsPublic(){
		panic("java.lang.IllegalAccessError")
	}

	if method.IsStatic() || method.IsPrivate() {
		// 考虑接口里的静态方法和私有方法和默认方法的话太复杂了，这只是个简单的JVM，暂时不实现那部分
		panic("java.lang.IncompatibleClassChangeError")
	}
	// 要调用这个方法的实例
	object := frame.OperandStack().GetRefFromTop(method.ArgsSlotCount())
	if object == nil {
		panic("java.lang.NullPointerException")
	}

	// 如果object对象所属的类没有实现这个接口方法所在的接口，就报错
	if !object.Class().IsImplements(methodRef.ResolveClass()) {
		panic("java.lang.IncompatibleClassChangeError")
	}
	// 根据object对象实际所属的类，决定应该调用哪个方法
	methodToBeInvoke := heap.LookupMethodInClass(object.Class(),methodRef.Name(),methodRef.Descriptor())

	if methodToBeInvoke == nil ||methodToBeInvoke.IsAbstract() {
		panic("java.lang.AbstractMethodError")
	}

	base.InvokeMethod(frame,methodToBeInvoke)
}
